package com.xhwl.centiotdata.suppliersystem.eesd.iotnorth;

import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSONObject;
import com.xhwl.centiotdata.constant.EesdConstant;
import com.xhwl.centiotdata.utils.ReadJsonUtils;
import com.xhwl.centiotdata.utils.StrUtils;
import com.xhwl.common.dto.cent.eesd.EnvIotNorthDeviceDataDTO;
import com.xhwl.common.dto.cent.iotdata.iotnorth.*;
import com.xhwl.common.enums.device.DeviceOnlineStatusEnum;
import com.xhwl.common.enums.eesd.IotProductIdEnum;
import com.xhwl.common.exception.BusinessException;
import com.xhwl.common.pojo.cent.device.Device;
import com.xhwl.common.pojo.cent.device.DeviceProjSupplierCategory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description:
 * @Copyright: Copyright (c) 2021  ALL RIGHTS RESERVED.
 * @Company: 深圳市兴海物联科技有限公司
 * @Author: gyc
 * @CreateDate: 2021/10/10 17:05
 * @UpdateDate: 2021/10/10 17:05
 * @UpdateRemark: init
 * @Version: 1.0
 */
@Slf4j
public class IotNorthApiUtil {

    /**
     * 获取设备code列表
     * @return
     */
    public static List<String> getCodeList(DeviceProjSupplierCategory config) {
        Integer projectId = config.getProjectId();
        JSONObject properties = config.getProperties();
        String ipUrl = properties.getString("ip");
        String deviceListUrl = properties.getString("deviceListUrl");
        Integer count = (Integer) properties.get("count");
        String deviceUrl = ipUrl + deviceListUrl;

        
        IotNorthDeviceListReqDTO iotReqDTO = new IotNorthDeviceListReqDTO();
        iotReqDTO.setAll(false);
        iotReqDTO.setPage(1);
        iotReqDTO.setCount(count);
        List<Integer> projectIds = new ArrayList<>();
        projectIds.add(projectId);
        iotReqDTO.setProject(projectIds);

        // 获取设备列表
        String json = JSONObject.toJSONString(iotReqDTO);

        String result = null;
        List<String> codeList = null;
        try {
            result = HttpRequest.post(deviceUrl).body(json).execute().body();
            //log.info("获取设备编码列表 deviceUrl:{}, json:{}, result:{}", deviceUrl, json, result);
            String code = JSONObject.parseObject(result).get("code").toString();
            if (code.equals(EesdConstant.SUCCESS_1000_CODE)) {
                String data = JSONObject.parseObject(result).get("data").toString();
                List<IotNorthDeviceListRespDTO> list = JSONObject.parseArray(data, IotNorthDeviceListRespDTO.class);
                if (!CollectionUtils.isEmpty(list)) {
                    codeList = list.stream().map(IotNorthDeviceListRespDTO::getCode).collect(Collectors.toList());
                }
            }
        } catch (Exception e) {
            log.error("获取设备编码列表: 失败 {}", e.getMessage());
        }
        return codeList;
    }

    /**
     * 获取单个网关设备下的设备属性（设备影子） 原始数据
     * @param config cent-device配置文件
     * @param deviceCode 网关编号
     * @return
     */
    public static List<IotNorthDeviceShadowDTO> getShadowList(DeviceProjSupplierCategory config, String deviceCode) {
        Integer projectId = config.getProjectId();
        JSONObject properties = config.getProperties();
        String ipUrl = properties.getString("ip");
        String deviceShadowListUrl = properties.getString("deviceShadowListUrl");
        Integer count = (Integer) properties.get("count");
        String shadowListUrl = ipUrl + deviceShadowListUrl;

        List<IotNorthShadowListRespDTO> list = new ArrayList<>();

        IotNorthShadowListReqDTO dto = new IotNorthShadowListReqDTO();
        dto.setPage(1);
        dto.setCount(count);
        dto.setCode(deviceCode);
        String json = JSONObject.toJSONString(dto);
        String result = HttpRequest.post(shadowListUrl).body(json).execute().body();
        // 根据total来判断调几次
        Integer total = (Integer) JSONObject.parseObject(result).get("total");
        int j = total / 100;
        for (int i = 1; i < j +2; i++) {
            dto.setPage(i);
            dto.setCount(100);
            String req = JSONObject.toJSONString(dto);
            result = HttpRequest.post(shadowListUrl).body(req).execute().body();
            //log.info("获取传感器设备属性原始记录表列表 deviceUrl:{}, json:{}, result:{}", shadowListUrl, req, result);
            String code = JSONObject.parseObject(result).get("code").toString();
            if (code.equals(EesdConstant.SUCCESS_1000_CODE)) {
                String data = JSONObject.parseObject(result).get("data").toString();
                List<IotNorthShadowListRespDTO> dataList = JSONObject.parseArray(data, IotNorthShadowListRespDTO.class);
                list.addAll(dataList);
            }
        }
        // 拉取全部记录
        List<IotNorthDeviceShadowDTO> resultList = new ArrayList<>();

        for (IotNorthShadowListRespDTO iotNorthShadowListRespDTO : list) {
            String idStr = iotNorthShadowListRespDTO.getId();
            LocalDateTime now = LocalDateTime.now();
            // 传感器id
            if (idStr.contains("_")) {
                String[] str = idStr.split("_");
                String fieldName = str[0];
                String deviceId = str[1];

                // iot北向五合一传感器设备属性原始记录表
                IotNorthDeviceShadowDTO iotNorthDeviceShadow = new IotNorthDeviceShadowDTO();
                iotNorthDeviceShadow.setDeviceId(deviceId);
                iotNorthDeviceShadow.setFieldName(fieldName);
                iotNorthDeviceShadow.setGatewayId(dto.getCode());
                iotNorthDeviceShadow.setCurValue(iotNorthShadowListRespDTO.getCurValue());
                iotNorthDeviceShadow.setObsTime(now);
                iotNorthDeviceShadow.setProjectId(projectId);
                // 批量同步 iot北向五合一传感器设备属性原始记录 到
                resultList.add(iotNorthDeviceShadow);
            }
        }
        log.info("环境EESD获取iot北向五合一传感器设备属性原始记录表列表:{}", resultList.size());
        List<String> removeDeviceList = properties.getObject("removeDeviceList", ArrayList.class);
        if (!CollectionUtils.isEmpty(removeDeviceList)) {
        	/**
        	 * 不能网关下可能绑定的设备id可以是相同的, 会导致过滤掉正常的数据值
        	 *  这里修改为根据网关code+deviceId进行过滤
        	 *  2022-05-17
        	 */
            resultList = resultList.stream().filter(i -> !removeDeviceList.contains(deviceCode + "_" + i.getDeviceId())).collect(Collectors.toList());
        }
        //log.info("环境EESD获取iot北向五合一传感器设备属性排查后列表:{}", resultList.size());
        return resultList;
    }

    /**
     *
     * 把分散的小list片段组合成对象  List<EnvDataVO> envObjectList = getEnvObject(deviceCode, resultList);
     * @param productId device_product 产品id
     * @param projectId 项目id
     * @param deviceCode 网关id
     * @param iotNorthDeviceShadows 网关下面的所有传感器
     * @return
     */
    public static List<EnvIotNorthDataDTO> getEnvObject(Integer productId, Integer projectId, String deviceCode, List<IotNorthDeviceShadowDTO> iotNorthDeviceShadows) {
        Map<String, List<IotNorthDeviceShadowDTO>> listMap = iotNorthDeviceShadows.stream().collect(Collectors.groupingBy(IotNorthDeviceShadowDTO::getDeviceId));
        List<EnvIotNorthDataDTO> list = new ArrayList<>();
        List<String> notUserList = new ArrayList<>();
        // now拉取时间：放在循环外面  保证这一批是同一时间
        LocalDateTime now = LocalDateTime.now();

        for (Map.Entry<String, List<IotNorthDeviceShadowDTO>> entry : listMap.entrySet()) {
            String key = entry.getKey();
            List<IotNorthDeviceShadowDTO> value = entry.getValue();
            // iot返回数据有userd及当前值是true则改设备是使用的
            boolean present = value.stream().anyMatch(v -> v.getFieldName().equals(EesdConstant.USED) && v.getCurValue().equals(EesdConstant.ONLINE_TRUE_STATUS));
            // true 是使用，false 未使用
            if (present) {
                // 设备id为 网关_传感器地址
                EnvIotNorthDataDTO envIotNorthDataDTO = new EnvIotNorthDataDTO();
                // 设备id为 网关_传感器地址
                String deviceId = deviceCode + "_" + key;
                envIotNorthDataDTO.setProjectId(projectId);
                envIotNorthDataDTO.setDeviceId(deviceId);
                envIotNorthDataDTO.setName(key);
                // 产品id
                envIotNorthDataDTO.setProductId(productId);
                // 以拉取时间为观测时间
                envIotNorthDataDTO.setObsTime(now);
                for (IotNorthDeviceShadowDTO iotNorthDeviceShadow : value) {
                    // 属性字段
                    String fieldName = iotNorthDeviceShadow.getFieldName();
                    // 传感器设备属性值
                    String curValue = iotNorthDeviceShadow.getCurValue();
                    switch (fieldName) {
                        case "online" :
                            // 判断环境设备在离线状态
                            if (EesdConstant.ONLINE_TRUE_STATUS.equals(curValue)) {
                                envIotNorthDataDTO.setDeviceStatus(1);
                            } else {
                                envIotNorthDataDTO.setDeviceStatus(0);
                            }
                            break;
                        case "pm2.5" :
                            if (StrUtils.isNumber(curValue)) {
                                envIotNorthDataDTO.setPm2p5(Double.valueOf(curValue));
                            } else {
                                envIotNorthDataDTO.setPm2p5(EesdConstant.DEFAULT_0D_VALUE);
                            }
                            break;
                        case "formaldehyde" :
                            if (StrUtils.isNumber(curValue)) {
                            	/**
                            	 * 需求说明
										1. 从环境设备处取到的甲醛原始值单位从mg/m³改为了ug/m³；
										2. 需要后端对甲醛进行单位换算，将取到的甲醛值/1000换算成mg/m³，进行了单位换算的值，再进行等级判断，及给到前端展示：
                            	 * 处理方案: 
                            	 * 		2022-05-09 
                            	 *  	与产品及前开发沟通确定, 目前只有南投在使用此功能.
                            	 * 		这里不需要根据项目判断是否需要除以1000.
                            	 */
                                envIotNorthDataDTO.setCh2o(Double.valueOf(curValue)/1000);
                            } else {
                                envIotNorthDataDTO.setCh2o(EesdConstant.DEFAULT_0D_VALUE);
                            }
                            break;
                        case "co2" :
                            if (StrUtils.isNumber(curValue)) {
                                envIotNorthDataDTO.setCo2(Double.valueOf(curValue));
                            } else {
                                envIotNorthDataDTO.setCo2(EesdConstant.DEFAULT_0D_VALUE);
                            }
                            break;
                        case "temp" :
                            if (StrUtils.isNumber(curValue)) {
                                envIotNorthDataDTO.setTemp(Double.valueOf(curValue));
                            } else {
                                envIotNorthDataDTO.setTemp(EesdConstant.DEFAULT_0D_VALUE);
                            }
                            break;
                        case "humidity" :
                            if (StrUtils.isNumber(curValue)) {
                                envIotNorthDataDTO.setHumidity(Double.valueOf(curValue));
                            } else {
                                envIotNorthDataDTO.setHumidity(EesdConstant.DEFAULT_0D_VALUE);
                            }
                            break;
                        default:
                    }
                }
                list.add(envIotNorthDataDTO);
            } else {
                notUserList.add(key);
            }
        }
        return list;
    }

    /**
     * 环境判断等级方法:目前只有温、湿度、二氧化碳、甲醛、pm2.5判断
     * @param envDataVOList
     */
    public static List<EnvIotNorthDeviceDataDTO> envJudgmentLevel(List<EnvIotNorthDataDTO> envDataVOList) {
        List<EnvIotNorthDeviceDataDTO> list = new ArrayList<>();
        for (EnvIotNorthDataDTO envDataVO : envDataVOList) {
            EnvIotNorthDeviceDataDTO envIotNorthDeviceDataDTO = new EnvIotNorthDeviceDataDTO();
            BeanUtils.copyProperties(envDataVO, envIotNorthDeviceDataDTO);

            Double temp = envDataVO.getTemp();
            Double humidity = envDataVO.getHumidity();
            Double co2 = envDataVO.getCo2();
            Double ch2o = envDataVO.getCh2o();
            Double pm2p5 = envDataVO.getPm2p5();
            if (temp <= 8) {
                envIotNorthDeviceDataDTO.setTempCategory("寒冷");
            } else if (temp < 15) {
                envIotNorthDeviceDataDTO.setTempCategory("偏冷");
            } else if (temp < 27) {
                envIotNorthDeviceDataDTO.setTempCategory("舒适");
            } else if (temp < 30) {
                envIotNorthDeviceDataDTO.setTempCategory("偏热");
            } else {
                envIotNorthDeviceDataDTO.setTempCategory("炎热");
            }

            if (humidity <= 30) {
                envIotNorthDeviceDataDTO.setHumidityCategory("干燥");
            } else if (humidity < 75) {
                envIotNorthDeviceDataDTO.setHumidityCategory("舒适");
            } else {
                envIotNorthDeviceDataDTO.setHumidityCategory("潮湿");
            }

            if (co2 <= 1000) {
                envIotNorthDeviceDataDTO.setCo2Category("空气清新");
            } else if (co2 < 1500) {
                envIotNorthDeviceDataDTO.setCo2Category("轻度污染");
            } else {
                envIotNorthDeviceDataDTO.setCo2Category("重度污染");
            }

            if (ch2o <= 0.1) {
                envIotNorthDeviceDataDTO.setCh2oCategory("正常");
            } else if (0.1 < ch2o && ch2o <= 0.3) {
                envIotNorthDeviceDataDTO.setCh2oCategory("轻度污染");
            } else if (0.3 < ch2o && ch2o < 2.0) {
                envIotNorthDeviceDataDTO.setCh2oCategory("中度污染");
            } else {
                envIotNorthDeviceDataDTO.setCh2oCategory("重度污染");
            }

            if (pm2p5 <= 35) {
                envIotNorthDeviceDataDTO.setPm2p5Category("优");
            } else if (pm2p5 <= 75) {
                envIotNorthDeviceDataDTO.setPm2p5Category("良");
            } else if (pm2p5 <= 115) {
                envIotNorthDeviceDataDTO.setPm2p5Category("轻度污染");
            } else if (pm2p5 <= 150) {
                envIotNorthDeviceDataDTO.setPm2p5Category("中度污染");
            } else if (pm2p5 <= 250) {
                envIotNorthDeviceDataDTO.setPm2p5Category("重度污染");
            } else {
                envIotNorthDeviceDataDTO.setPm2p5Category("严重污染");
            }

            if (temp <= 25 && humidity <= 75 && ch2o <= 0.1 && co2 <= 1000 && pm2p5 <= 35) {
                envIotNorthDeviceDataDTO.setCategory("优");
            } else if (ch2o > 0.3 || co2 > 1500 || pm2p5 > 150){
                envIotNorthDeviceDataDTO.setCategory("差");
            } else {
                envIotNorthDeviceDataDTO.setCategory("良好");
            }
            list.add(envIotNorthDeviceDataDTO);
        }
        return list;
    }



    /**
     * iot拉取项目所有设备
     * @param config
     * @return
     */
    static List<Device> getDeviceListV1(DeviceProjSupplierCategory config) {
        Integer projectId = config.getProjectId();
        JSONObject properties = config.getProperties();
        Integer productId = config.getProductId();
        String ipUrl = properties.getString("ip");
        String deviceTypeCode = Optional.ofNullable(properties.getString("deviceTypeCode")).orElse("Zero");
        String deviceListUrl = properties.getString("deviceListUrl");

        String deviceUrl = ipUrl + deviceListUrl;

        List<IotNorthDeviceListRespDTO> resultList = new ArrayList<>();
        List<Device> list = new ArrayList<>();
        int total;
        do {
            int pageNum = EesdConstant.PAGE_NUM;

            Map<String, Object> map = new HashMap<>();
            map.put("all", false);
            map.put("count", EesdConstant.SIZE);
            map.put("page", pageNum);
            map.put("project", Arrays.asList(projectId));
            //log.info("兴海-iot获取设备请求参数：{}", JSONObject.toJSONString(map));

            String result = HttpRequest.post(deviceUrl).body(JSONObject.toJSONString(map)).execute().body();

            total = Optional.ofNullable(result)
                    .map(r -> JSONObject.parseObject(r)
                    .getInteger(EesdConstant.TOTAL))
                    .orElseThrow(() -> new BusinessException("兴海-iot拉取设备失败total"));
            List<IotNorthDeviceListRespDTO> data = Optional.ofNullable(result)
                    .map(r -> JSONObject.parseObject(r).getString(EesdConstant.DATA))
                    .map(d -> JSONObject.parseArray(d, IotNorthDeviceListRespDTO.class))
                    .orElseThrow(() -> new BusinessException("兴海-iot拉取设备失败data"));
            resultList.addAll(data);
            ++pageNum;
        } while (total != resultList.size());

        if (!CollectionUtils.isEmpty(resultList)) {
            for (IotNorthDeviceListRespDTO dto : resultList) {
                IotProductIdEnum iotProductIdEnum = IotProductIdEnum.get(dto.getProductCode());
                Integer deviceTypeId = iotProductIdEnum.getCode();
                String deviceType = iotProductIdEnum.getDeviceTypeCode();
                if (deviceTypeId == 0 || !deviceType.equals(deviceTypeCode)) {
                    continue;
                }
                Device device = new Device();
                device.setProjectId(projectId);
                // 三方id
                device.setThirdDeviceId(dto.getCode());
                device.setName(dto.getName());
                // 设备状态
                if (dto.getOnlineStatus()) {
                    device.setOnlineStatus(DeviceOnlineStatusEnum.ONLINE);
                } else {
                    device.setOnlineStatus(DeviceOnlineStatusEnum.OFFLINE);
                }
                // 设备类型id
                device.setDeviceTypeId(deviceTypeId);
                device.setProductId(productId);
                list.add(device);
            }
        }
        return list;
    }

    /**
     * iot拉取项目所有设备
     * @param config
     * @return
     */
    static List<Device> getDeviceListV2(DeviceProjSupplierCategory config) {
        Integer projectId = config.getProjectId();
        JSONObject properties = config.getProperties();
        Integer productId = config.getProductId();
        String ipUrl = properties.getString("ip");
        String deviceTypeCode = Optional.ofNullable(properties.getString("deviceTypeCode")).orElse("Zero");
        String deviceListUrl = properties.getString("deviceListUrl");

        String deviceUrl = ipUrl + deviceListUrl;

        List<IotNorthDeviceListRespV2DTO> resultList = new ArrayList<>();
        List<Device> list = new ArrayList<>();
        int total;
        do {
            int pageNum = EesdConstant.PAGE_NUM;

            Map<String, Object> map = new HashMap<>();
            map.put("all", false);
            map.put("count", EesdConstant.SIZE);
            map.put("page", pageNum);
            map.put("projectId", Arrays.asList(projectId));
            //log.info("兴海-iot获取设备请求参数：{}", JSONObject.toJSONString(map));

            String returnJson = HttpRequest.post(deviceUrl).body(JSONObject.toJSONString(map)).execute().body();
            String result = JSONObject.parseObject(returnJson).get("result").toString();
            total = Optional.ofNullable(result)
                    .map(r -> JSONObject.parseObject(r)
                            .getInteger(EesdConstant.TOTAL))
                    .orElseThrow(() -> new BusinessException("兴海-iot拉取设备失败total"));
            List<IotNorthDeviceListRespV2DTO> data = Optional.ofNullable(result)
                    .map(r -> JSONObject.parseObject(r).getString(EesdConstant.RECORDS))
                    .map(d -> JSONObject.parseArray(d, IotNorthDeviceListRespV2DTO.class))
                    .orElseThrow(() -> new BusinessException("兴海-iot拉取设备失败data"));
            resultList.addAll(data);
            ++pageNum;
        } while (total != resultList.size());

        if (!CollectionUtils.isEmpty(resultList)) {
            for (IotNorthDeviceListRespV2DTO dto : resultList) {
                IotProductIdEnum iotProductIdEnum = IotProductIdEnum.get(dto.getProductCode());
                Integer deviceTypeId = iotProductIdEnum.getCode();
                String deviceType = iotProductIdEnum.getDeviceTypeCode();
                if (deviceTypeId == 0 || !deviceType.equals(deviceTypeCode)) {
                    continue;
                }
                Device device = new Device();
                device.setIotDeviceId(dto.getId().toString());
                device.setProjectId(projectId);
                // 三方id
                device.setNodeId(dto.getDeviceCode());
                device.setName(dto.getDeviceName());
                // 设备状态
                if (dto.getOnline().equals(2)) {
                    device.setOnlineStatus(DeviceOnlineStatusEnum.ONLINE);
                } else {
                    device.setOnlineStatus(DeviceOnlineStatusEnum.OFFLINE);
                }
                // 设备类型id
                device.setDeviceTypeId(deviceTypeId);
                device.setProductId(productId);
                list.add(device);
            }
        }
        return list;
    }


    /**
     * 获取单个设备字段及对应属性V1
     * @param config
     * @return k-id,v-curValue
     */
    public static Map<String, Object> getShadowListV1(DeviceProjSupplierCategory config, String deviceCode) {
        JSONObject properties = config.getProperties();
        String ipUrl = properties.getString("ip");
        String deviceShadowListUrl = properties.getString("deviceShadowListUrl");
        String shadowListUrl = ipUrl + deviceShadowListUrl;
        List<IotNorthShadowListRespDTO> list = new ArrayList<>();
        int total;
        do {
            IotNorthShadowListReqDTO dto = new IotNorthShadowListReqDTO();
            int pageNum = EesdConstant.PAGE_NUM;
            dto.setPage(pageNum);
            dto.setCount(EesdConstant.SIZE);
            dto.setCode(deviceCode);
            String json = JSONObject.toJSONString(dto);
            String result = HttpRequest.post(shadowListUrl).body(json).execute().body();

            total = Optional.ofNullable(result)
                    .map(r -> JSONObject.parseObject(r)
                    .getInteger(EesdConstant.TOTAL))
                    .orElseThrow(() -> new BusinessException("兴海-iot拉取属性值失败total"));
            List<IotNorthShadowListRespDTO> resultData = Optional.ofNullable(result)
                    .map(r -> JSONObject.parseObject(r).getString(EesdConstant.DATA))
                    .map(d -> JSONObject.parseArray(d, IotNorthShadowListRespDTO.class))
                    .orElseThrow(() -> new BusinessException("兴海-iot拉取属性值失败result"));
            list.addAll(resultData);
            ++pageNum;
        } while (list.size() != total);

        // 拉取全部记录
        Map<String, Object> map = new HashMap<>();
        for (IotNorthShadowListRespDTO resp : list) {
            map.put(resp.getId(), resp.getCurValue());
        }
        return map;
    }
}
